First steps with the pySPM library

Note that this is only a brief overview...

Loading the library


In [1]:
import pySPM
print(pySPM.__version__)


0.2.19

Let's set some variable and libraries to retrieve the data and plot them


In [2]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
%matplotlib inline

import os
from IPython import display

Let's import the pySPM_data library which provides all example data files. You can download it here https://github.com/scholi/pySPM_data in case you whish to run this Documentation script with jupyter


In [3]:
from pySPM_data import get_data

AFM Data

Nanoscan AFM loading

Let's load the data (forward topography by default) and plot it


In [4]:
filename = get_data("Circles_defects.xml")
scan = pySPM.Nanoscan(filename)
scan.list_channels()


forward
=======
  - Normal Deflection
  - FreeResAmpl
  - A_1st
  - Topography

backward
========
  - Normal Deflection
  - FreeResAmpl
  - A_1st
  - Topography


In [5]:
topo = scan.get_channel() # is equivalent to scan.get_channel('Topography',backward=False)
A1st = scan.get_channel("A_1st") 
print(scan.get_summary())

fig, ax = plt.subplots(1, 2, figsize=(14,7))
topo.show(ax=ax[0])
A1st.show(ax=ax[1], sig=2);


Feedback: A_1st : P:1.6m/m : I:3.0ms
Size: 256×256 pixels = 20.0 um×20.0 um
Scan Speed: 6.0s/line

Bruker AFM loading

Load a file and display a list of the recorded channels


In [6]:
filename = get_data("Smileys.001")
ScanB = pySPM.Bruker(filename)
ScanB.list_channels()


Channels
========
	S [ZSensor] "Height Sensor"
	S [] "Peak Force Error"
	S [Deformation] "Deformation"
	S [] "LogDMTModulus"
	S [Adhesion] "Adhesion"
	S [Deformation] "Deformation"
	S [Dissipation] "Dissipation"
	S [Height] "Height"

In [7]:
topoB = ScanB.get_channel()
adhesion = ScanB.get_channel("Adhesion")
deformation = ScanB.get_channel("Deformation")

fig, ax = plt.subplots(1, 3, figsize=(21, 7))
topoB.show(ax=ax[0])
adhesion.show(ax=ax[1])
deformation.show(ax=ax[2], cmap='jet', sig=2);


Bruker tends to save information only on the forward either backward scans. To simplify your life, the get_channel has an argument lazy=True which will switch automatically to forward/backward if no image is found for the backard/forward channel. If you wish to get absolutely the forward/backward channel and get an error if not found, please set lazy=False.

In the example below, the error raised demonstrate that the topography channel is only available for the backward scan!


In [8]:
try:
    ScanB.get_channel(lazy=False)
except Exception as e:
    display.display(display.HTML("<b style='color: red'>{}</b>: {}".format(type(e).__name__, e.args[0])))


Exception: Channel Height Sensor not found

nanonis XSM loading


In [9]:
filename = get_data("test.sxm")
S = pySPM.SXM(filename)
S.list_channels()
fig, ax = plt.subplots(1,2,figsize=(14,7))
S.get_channel('Z').show(ax=ax[0]);
p = S.get_channel('Current').show(ax=ax[1], cmap='viridis');


Channels
========
  - Z
  - Current

Accessing the raw data

Note that the RAW data are flipped upside-down compared to the plotted image (with topo.show() for e.g.)


In [10]:
topoB.pixels


Out[10]:
array([[25.21421677, 25.22343249, 25.21421677, ..., 21.91498811,
        21.91498811, 21.92420383],
       [25.19578532, 25.19578532, 25.19578532, ..., 21.89655666,
        21.89655666, 21.90577239],
       [25.17735388, 25.16813815, 25.16813815, ..., 21.87812522,
        21.87812522, 21.88734094],
       ...,
       [14.04476109, 14.03554537, 14.03554537, ..., 10.72710099,
        10.73631671, 10.75474816],
       [14.02632965, 14.02632965, 14.01711393, ..., 10.70866955,
        10.72710099, 10.73631671],
       [13.99868248, 13.98946676, 13.98946676, ..., 10.68102238,
        10.69945382, 10.70866955]])

In [11]:
fig, ax = plt.subplots(1, 2, figsize=(14, 7))
ax[0].imshow(topoB.pixels, cmap='hot');
topoB.show(ax=ax[1], pixels=True, flip=True);


Data correction

All the above technique can generate an SPM_image out of raw data. The SPM_image propose several functions to correct/plot the data

Basic corrections


In [12]:
topo2 = topoB.correct_lines(inline=False)
topo3 = topoB.correct_plane(inline=False)

fig, ax = plt.subplots(1,3,figsize=(21, 7))
topoB.show(ax=ax[0])
ax[0].set_title("Original image")
topo2.show(ax=ax[1])
ax[1].set_title("Corrected by lines")
topo3.show(ax=ax[2])
ax[2].set_title("Corrected by slope");


Median of differences


In [13]:
fig = plt.figure(figsize=(7,7))
import copy
topo4 = copy.deepcopy(topoB) # make deepcopy of object otherwise you will just change the original
topo4.correct_median_diff()
topo4.show();


Scars removal

We should now remove the scars from the image


In [14]:
fig, ax = plt.subplots(1, 2, figsize=(14, 7))

topoC = topo4.filter_scars_removal(.7,inline=False)
topo4.show(ax=ax[0]);
topoC.show(ax=ax[1]);
for a in ax:
    for p in [(71,50),(10,13),(32,13),(80,5)]:
        a.annotate("", p, (p[0]+5, p[1]+5), arrowprops=dict(arrowstyle="->", color='w'));


Remove polynomial background


In [15]:
from skimage.morphology import binary_erosion, disk
topoD = topoC.corr_fit2d(inline=False).offset([[10, 0, 10, 511]]).filter_scars_removal()

mask0 = topoD.get_bin_threshold(.1, high=False)
mask1 = binary_erosion(mask0, disk(3))
topoD2 = topoC.corr_fit2d(mask=mask1, inline=False).offset([[10, 0, 10, 511]]).filter_scars_removal().zero_min()

In [16]:
fig, ax = plt.subplots(2, 3, figsize=(21, 14))
ax = np.ravel(ax)
topoC.show(ax=ax[0])
ax[0].set_title("Image: median_diff correction")
topoD.show(ax=ax[1], sig=1.5);
ax[1].set_title("Polynomial correction on whole image")
ax[2].imshow(mask0);
ax[2].set_title("Mask: binary thersholding")
ax[3].imshow(mask1);
ax[3].set_title("Mask: background selection");
topoD2.show(ax=ax[4], pixels=True);
ax[4].set_title("Polynomial correction on background mask");
topoD2.show(ax=ax[5], adaptive=True);


Flatten along path

A better way to correct the data is by defining pathes that are known to be flat and each line will be offseted automatically in order to get the defines lines as flat as possible. First let's display the data with the pixel values in the axis instead of the real size. The pixels coordinated will be used to define the path.


In [17]:
fig, ax = plt.subplots(1, 2, figsize=(14, 7))

topoD = topoB.filter_scars_removal(inline=False).offset([
    [10,0,10,511],
    [70,0,170,100],
    [240,240,160,340]], ax=ax[0], width=10, alpha=.2, axPixels=True)
topoB.show(ax=ax[0], pixels=True)
topoD.show(ax=ax[1], pixels=True);


Plotting AFM or ToF-SIMS images


In [18]:
fig, (ax, ax2) = plt.subplots(2, 3, figsize=(21, 14))
topoD.show(ax=ax[0], cmap='gray', title="color map=\"gray\"")
topoD.show(ax=ax[1], sig=2, title="standard deviation=2")
topoD.show(ax=ax[2], adaptive=True, title="Adaptive colormap")
topoD.show(ax=ax2[0], dmin=4e-8, cmap='gray', title="raise the lowest value for the colormap of +40nm")
topoD.show(ax=ax2[1], dmin=3e-8, dmax=-3e-8, cmap='gray',title="raise lower of +30nm and highest of -30nm")
topoD.show(ax=ax2[2], pixels=True, title="Set axis value in pixels");


Getting a profile

With coordinates in pixels


In [19]:
fig, ax = plt.subplots(1,2,figsize=(10,5))

topoD2.plot_profile(280,250,400,208,ax=ax[1],img=ax[0])
topoD2.show(ax=ax[0],pixels=True)
pySPM.utils.Ydist(plt.gca(), .14, 1.22, 27, unit="nm")
plt.tight_layout()


With coordinates in the real units (as in the image axis)


In [20]:
fig, ax = plt.subplots(1,3,figsize=(21,5))

topoD2.show(ax=ax[0])
topoD2.plot_profile(50, 47, 75, 57, ax=ax[1], img=ax[0], pixels=False);
topoD2.plot_profile(50, 47, 75, 57, ax=ax[2], pixels=False);
ax[2].set_title("ztransf can be used to adj. the z-scale.\n Here in nm with the minimum at 0");
ax[2].grid();
ax[2].yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator()) # set minor ticks every units
ax[2].xaxis.set_minor_locator(mpl.ticker.AutoMinorLocator()) # set minor ticks automatically
ax[2].grid(axis='both',which='minor',alpha=.2); # display the minor grid every nm vertically
plt.tight_layout();


ToF-SIMS Data

ToF-SIMS Data loading (ITA images)

Load an image and show the list of the recorded peaks (channels). Displayed is the assignment name (if any) then three numbers representing the lower, center and upper mass of the peak


In [21]:
TOF = pySPM.ITA(get_data("BigSmiley.ita"))
TOF.show_masses()


0: (total) [] 0.00u - 309.94u (center: 154.97u)
1: (sum of rest) [] 0.00u - 309.94u (center: 154.97u)
2: () [CH-] 12.99u - 13.03u (center: 13.01u)
3: () [O-] 15.97u - 16.03u (center: 15.99u)
4: () [OH-] 16.98u - 17.03u (center: 17.00u)
5: () [F-] 18.97u - 19.03u (center: 19.00u)
6: () [C_2-] 23.97u - 24.03u (center: 24.00u)
7: () [C_2H-] 24.98u - 25.04u (center: 25.01u)
8: () [CN-] 25.97u - 26.04u (center: 26.00u)
9: () [BO-] 26.97u - 27.04u (center: 27.00u)
10: () [Si-] 27.94u - 28.01u (center: 27.98u)
11: () [SiH-] 28.95u - 29.02u (center: 28.98u)
12: () [^30Si-] 29.94u - 30.02u (center: 29.99u)
13: () [O_2-] 31.95u - 32.03u (center: 31.97u)
14: () [PH_2-] 32.95u - 33.03u (center: 32.98u)
15: () [Cl-] 34.93u - 35.01u (center: 34.97u)
16: () [^37Cl-] 36.93u - 37.01u (center: 36.97u)
17: () [C_2O-] 39.96u - 40.04u (center: 40.00u)
18: () [BNO-] 40.97u - 41.05u (center: 41.00u)
19: () [CHNO-] 42.97u - 43.05u (center: 43.00u)
20: () [CHO_2-] 44.95u - 45.04u (center: 45.00u)
21: () [] 59.91u - 60.04u (center: 59.98u)
22: () [] 60.94u - 61.04u (center: 60.98u)
23: () [] 75.93u - 76.05u (center: 75.98u)
24: () [] 76.94u - 77.04u (center: 76.98u)
25: () [Au-] 196.80u - 197.10u (center: 196.98u)
26: () [Cs-] 132.81u - 133.01u (center: 132.93u)

Retriving ToF-SIMS channel by Mass

The ToF-SIMS images save each scan for each channel separately. In order to save time the sum of all scan is also saved and can be retrieved with the getAddedImageByMass or getAddedImageByName function.


In [22]:
fig, ax = plt.subplots(1,3,figsize=(21,7))

TOF.get_added_image_by_mass(19).show(cmap='hot',ax=ax[0])
img, ch = TOF.get_added_image_by_name('Au')
img.show(cmap='hot',ax=ax[1]);
img.show(cmap='hot',ax=ax[2], flip=True);


Retriving ToF-SIMS channels by Name

The name can be searched with a regular expression in order to find specific channels.


In [23]:
fig, ax = plt.subplots(1, 3, figsize=(21, 7))

# At least one Carbon
C, ch = TOF.get_added_image_by_name("C[^a-z]")
TOF.show_masses(ch)

# Two carbons Carbon
C2,_ = TOF.get_added_image_by_name("C_2")

FCl,_ = TOF.get_added_image_by_name(["Cl","F"])

C.show(ax=ax[0], wrap=26, flip=True); # wrap allows the title to be on multiple lines (its value is the number of chars per line)
C2.show(ax=ax[1], flip=True);
FCl.show(ax=ax[2], flip=True);


2: () [CH-] 12.99u - 13.03u (center: 13.01u)
6: () [C_2-] 23.97u - 24.03u (center: 24.00u)
7: () [C_2H-] 24.98u - 25.04u (center: 25.01u)
8: () [CN-] 25.97u - 26.04u (center: 26.00u)
17: () [C_2O-] 39.96u - 40.04u (center: 40.00u)
19: () [CHNO-] 42.97u - 43.05u (center: 43.00u)
20: () [CHO_2-] 44.95u - 45.04u (center: 45.00u)

Performing overlays


In [24]:
C = pySPM.Collection() # Create a collection of images
C['Au'] = TOF.get_added_image_by_mass(197)
img, ch = TOF.get_added_image_by_name('CH')
C['Carb'] = img
C['Cl'] = TOF.get_added_image_by_name('Cl')[0]
Ov,Ovs = C.overlay(['Carb','Au','Cl'],[[1,0,0],[0,1,0],[0,0,1]])

fig, ax = plt.subplots(1,4,figsize=(21,7))
Ov.show(ax=ax[0], flip=True)
for i,O in enumerate(Ovs, start=1):
    O.show(ax=ax[i], flip=True);


Get cross-section


In [25]:
fig, ax = plt.subplots(1, 3, figsize=(21, 7))
Au = TOF.get_added_image_by_mass([35,37])
Au.show(ax=ax[0], pixels=True);
Au.plot_profile(0, 450, 511, 40, ax=ax[1], img=ax[0], imgColor='c');
ax[2].imshow(TOF.get_xsection_by_mass(0, 450, 511, 40, [35, 37]));


Spectra


In [26]:
Path = get_data("Cysteine_B3p_p_01_0.ita")
A = pySPM.ITA(Path)
sf, k0 = A.auto_mass_cal()
fig, ax = plt.subplots(4, 1,figsize=(21, 14))

A.show_spectrum(high=150, ax=ax[0], sf=sf, k0=k0)
#A.showSpectrum(low=56.5,high=57.5, ax=ax[0], sf=sf, k0=k0)
A.show_spectrum_around('C2H3NO', ax=ax[1], sf=sf, k0=k0);
# Add the mass C2H3NO to the mass calibration
sf, k0 = A.auto_mass_cal(fitting_peaks="C,CH,CH2,CH3,Na,C2H3NO", sf=sf, k0=k0)
A.show_spectrum_around('C2H3NO', ax=ax[2], sf=sf, k0=k0); # Shift the mass calibration to match the peaks
A.show_spectrum_around('C2H3NO', ax=ax[3], dofit=True, sf=sf, k0=k0); # Shift the mass calibration to match the peaks


c:\users\ols\dropbox\python\pyspm\pySPM\utils\math.py:114: UserWarning: Parameter Amp is deprecated. Please use amp in order to set the amplitude!
  warn("Parameter Amp is deprecated. Please use amp in order to set the amplitude!")

Collections

SPM and ToF-SIMS data contains usually several images for different channels for the same measurement. This library provides a class Collection which can handles that easilly.

Collections for ToF-SIMS images


In [27]:
TOFC = pySPM.ITA_collection(get_data("BigSmiley.ita"))
TOFC.show(ncols=4,wrap=30,flip=True)
plt.tight_layout();


Overlay


In [28]:
fig, ax = plt.subplots(1, 5, figsize=(21,7))

TOFC['carbs'] = TOFC.ita.get_added_image_by_name("C[^a-z]")[0]
channels = ['carbs', 'Au-', 'Cl-']
O,ch = TOFC.overlay(channels, vmin=0, sig=4)
ax[2].set_title("Overlay: "+", ".join(channels))
O.show(ax=ax[3], flip=True)
TOFC['Au-'].add_scale(50e-6, height=10, ax=ax[3], loc=3);
for i,x in enumerate(ch):
    x.show(ax=ax[i], flip=True);
pySPM.collection.overlay_triangle(channels, ax=ax[4], fontsize=16, proportion=.7)


PCA

The screeplot will help you to remember how many PC are important. But first you have to runPCA()


In [29]:
TOFC.run_pca()
TOFC.PCA.screeplot()



In [30]:
TOFC.show_pca(num=3, flip=True)
TOFC.loadings(num=3)


Out[30]:
CH- O- OH- F- C_2- C_2H- CN- BO- Si- SiH- ... BNO- CHNO- CHO_2- 59.98u 60.98u 75.98u 76.98u Au- Cs- carbs
PC1 0.147606 -0.174701 -0.024615 -0.028671 0.218547 0.242063 0.278734 0.016956 -0.130335 -0.065548 ... 0.277558 0.109676 0.289883 -0.252492 -0.221248 -0.258282 -0.240137 0.188259 -0.000049 0.296931
PC2 0.127242 0.386123 0.393833 0.082679 0.108985 0.151643 0.119182 0.124744 0.147823 0.091523 ... 0.160929 0.269395 0.192353 0.277084 0.245306 0.253710 0.241293 -0.286175 0.005997 0.219011
PC3 -0.029553 -0.017166 0.002484 0.008520 -0.042301 0.005293 -0.057747 0.093330 0.064018 0.056011 ... -0.156943 0.006441 -0.179935 0.022841 0.041551 0.016524 0.014799 0.017612 0.004949 -0.138978

3 rows × 26 columns

Here on top the negative loading are in blue and the positive in red (default). You can change the colormap (e.g. cmap='seismic') to get other contrasts. By default the colormap is symmetric so that the middle color equal to zero. I also like to get the laodings as a hinton map as follow:


In [31]:
fig = plt.figure(figsize=(21,2))
TOFC.PCA.hinton(matrix=TOFC.loadings(num=3))


Red are for the positive loadings and blue for the negative ones. The size of each square is proportional to their intensity.

If you wish to get two images per PC, one for the positive part and the other for the negative one, then use pn=True in showPCA:


In [32]:
TOFC.show_pca(num=3, flip=True, pn=True)


ToF-SIMS new ITAX fileformat

The new ITAX fileformat of Iontof is on ots way of beeing supported. For the moment it is still in development and can retrieve only:

  • ITAX.getProfile()
  • ITAX.getSpectrum()
  • ITAX.getSnapshots()

ToF-SIMS, ITM files and data reconstruction

ITM saved raw data which can be used for reconstruction and other low level data recovery

Retrieving Measurement Data

Some data are saved at the beginning of the measurement and at the end. They can be displayed as a table as follow:


In [33]:
I = pySPM.ITM(get_data("BigSmiley.itm"))
I.show_values(names=['Instrument.LMIG.Aperture_1','Instrument.LMIG.Extractor','Instrument.LMIG.Suppressor','Instrument.LMIG.EmissionCurrent','Experiment.AcquisitionTime'])


Parameter NameValue @startValue @end
Instrument.LMIG.Aperture_120.2 nA
Instrument.LMIG.Extractor9000 V
Instrument.LMIG.Suppressor1169 V

You can also display them in a GUI in order to explore the data more conveniently:


In [34]:
#I.show_values(gui=True)

Retrieve Data variations during the measurement


In [35]:
fig, ax = plt.subplots(1,1,figsize=(20,7))
axb = pySPM.utils.dual_plot(ax,'C0','C1') # create a dual plot with blue (C0) data/axis on the left and orange (C1) on the right
I.show_meas_data(ax=ax, mul=1e6, scans=3) # Display a tick every 3 scans (top axis)
I.show_meas_data("Instrument.LMIG.Suppressor",ax=axb, color='C1', scans=False);
ax.set_ylim((.8, 1.2));
axb.set_ylim((1168,1172));


Retrieve the pixel order in a random exposure


In [36]:
fig, ax = plt.subplots(1, 3, figsize=(21, 7))
pixel_list, pixel_order,  blocks = I.get_raw_data(scan=0)

A = np.array(pixel_list)
distances = np.sqrt(np.sum((A[1:, :]-A[:-1, :])**2, axis=1))

ax[0].imshow(pixel_order, cmap='hsv');
ax[0].set_title("colormap of the pixel order")

ax[1].hist(distances/pixel_order.shape[0], bins=100);
ax[1].set_title("Distribution of the distance between\ntwo consecutive pixel in random order")
ax[1].set_xlabel("Ratio of the distance with the width [px/px]");
ax[1].set_xlim((0, np.sqrt(2)));

cm = plt.get_cmap('hsv')

N = 30
for i in range(N):
    ax[2].plot(A[i:i+2,0], A[i:i+2,1],'o-',color=cm(float(i)/N), linewidth=2);
ax[2].set_xlim((0,pixel_order.shape[1]))
ax[2].set_ylim((0,pixel_order.shape[0]));
ax[2].set_title("Order of the first {} pixels".format(N))
ax[2].set_xlabel("x-position [px]")
ax[2].set_ylabel("y-position [px]");
ax[2].set_aspect('equal');


raw Spectrum retrieval

Note that the reconstruction is quite slow for the moment, as the developer don't use it very often...


In [37]:
from IPython import display
masses, raw_spectrum = I.get_raw_spectrum(scans=range(2), prog=True) # get raw spectrum of the first 10 scans
display.clear_output()

fig = plt.figure(figsize=(20,7))
plt.plot(masses, raw_spectrum);


Zoom on a specific peak and fit the data


In [38]:
masses = I.channel2mass(np.arange(masses.size))

fig, ax = plt.subplots(1, 4, figsize=(21, 7))
pySPM.utils.showPeak(masses, raw_spectrum, pySPM.utils.get_mass('CH3'), .25, ax=ax[0]);
pySPM.utils.showPeak(masses, raw_spectrum, pySPM.utils.get_mass('Cl'), .25, ax=ax[1]);
pySPM.utils.showPeak(masses, raw_spectrum, pySPM.utils.get_mass('Au'), .25, ax=ax[2], polarity='-');
pySPM.utils.showPeak(masses, raw_spectrum, pySPM.utils.get_mass('Au'), .25, ax=ax[3], include_only='Au');


c:\users\ols\dropbox\python\pyspm\pySPM\utils\math.py:114: UserWarning: Parameter Amp is deprecated. Please use amp in order to set the amplitude!
  warn("Parameter Amp is deprecated. Please use amp in order to set the amplitude!")

The warning message can be easily ignored. It comes from the fact that the calibration values can be written at different places. When the values are missing in the first place, the the alternative place is used and a warning message is raised. You can suppress the warning as follow if needed:


In [39]:
import warnings
warnings.simplefilter("ignore")
sf, k0 = I.get_mass_cal()
warnings.simplefilter("always")

SI image reconstruction

We will use here only the two first scans in order to save time


In [40]:
from tqdm import tqdm_notebook as tqdm
import cProfile

delta = .1
Na, Ag = I.reconstruct([(pySPM.utils.get_mass(x+'+')-delta, pySPM.utils.get_mass(x+'+')+delta) for x in ['Cl', 'Au']], scans=[0, 1], prog=True)

fig, ax = plt.subplots(1, 2, figsize=(15, 7))
Na.show(ax=ax[0]);
Ag.show(ax=ax[1]);



Rewrite ITA files

Blocks contains in the ITA/ITM fileformat can now be edited by the script.

For safety reason don't edit your files without backups! We highly recomend the users to copy their files before editing them.

In [41]:
from shutil import copyfile

copyfile(get_data("BigSmiley.ita"), "temp.ita");
t = pySPM.ITA('temp.ita')
Editing overwrite blocks in the case the size if the new data match the old data length. If this is not the case, a new block is created with the new data and the link in the parent folder will be changed to the new data. This means that the old block will be still present in the data, but not accessible by iontof anymore. Be careful, becaus this can makes your file grow fast if your rewrite often blocks with different sizes.

In order to open the file in readonly in order to avoid any editing by mistake, plese use the parameter readonly=True when you open a file


In [42]:
ro = pySPM.ITA("temp.ita", readonly=True)

# Let's look for the mass interval (mi) for the CH- channel
for x in ro.root.goto("MassIntervalList"):
    if x.name != 'mi':
        continue
    if x.goto("assign").get_string()=="CH-":
        break
# let's try to edit the desc value
print("desc: \"{}\"".format(x.goto("desc").get_string()))
try:
    x.edit_block("", "desc", "test".encode("utf16")[2:], force=True)
except Exception as e:
    display.display(display.HTML("<b style='color: red'>{}</b>: {}".format(type(e).__name__, e.args[0])))
    
print("desc: \"{}\"".format(x.goto("desc").get_string()))


desc: ""
UnsupportedOperation: write
desc: ""

But this would work fine with a writable file


In [43]:
# Let's look for the mass interval (mi) for the CH- channel
for x in t.root.goto("MassIntervalList"):
    if x.name != 'mi':
        continue
    if x.goto("assign").get_string()=="CH-":
        break
# let's try to edit the desc value
print("desc: \"{}\"".format(x.goto("desc").get_string()))
x.edit_block("", "desc", "test".encode("utf16")[2:], force=True)
print("desc: \"{}\"".format(x.goto("desc").get_string()))


desc: ""
desc: "test"

In [44]:
img = 255*pySPM.utils.misc.smiley(512, ratio=.6, eye=.2, mouth_rad=.65, mouth_thick=.25, eye_sep=.27, eye_height=.4)
plt.imshow(img)
miblock = t.create_new_miblock("", desc="smiley", peaklabel=1)
t.add_new_images(miblock, Added=img);



In [45]:
t.get_added_image_by_name("smiley")[0].show(flip=True);


You can now open the file temp.ita with SurfaceLab and see a new channel called "smiley"

Various utilities

Math

Gaussian / Lorentzian and CDF


In [46]:
ax = pySPM.utils.sp(3) # create 3 sub-plots

x = np.arange(100)

ax[0].plot(x,pySPM.utils.Gauss(x,50,10,1), label='Gauss');
ax[0].plot(x,pySPM.utils.LG(x,50,10,1, lg=1), label='Lorentz')
ax[0].plot(x,pySPM.utils.LG(x,50,10,lg=.5), label='Lorentz-Gauss (50-50%)')
ax[0].legend();

ax[1].plot(x,pySPM.utils.CDF(x,30,5)-pySPM.utils.CDF(x,70,5), label="CDF")
ax[1].set_title("CDFs");

for i,g in enumerate([1,.5,.1]):
    for j,nu in enumerate([1,2]):
        ax[2].plot(x, pySPM.utils.logistic(x, x0=50, growth=g, nu=nu), label='growth={}, nu={}'.format(g, nu), color='C'+str(i+1), linestyle=['-','--',':'][j]);
ax[2].legend();
ax[2].set_title("Logistic function (can fit most of step functions)");


Plotting


In [47]:
Y, X = np.mgrid[:500,:500]
R = np.sqrt((X-250)**2+(Y-250)**2)
LeftI= (np.sqrt((X-150)**2+(Y-150)**2)<50)
smiley = (R>=220)*(R<245)+LeftI+(np.sqrt((X-350)**2+(Y-150)**2)<50)+(R>100)*(R<150)*(Y>=250)

ax = pySPM.utils.sp(3)

ax[0].imshow(smiley);
pySPM.utils.plotMask(ax[0], LeftI, 'r'); # Let you paint a specific region by a specific color

x = np.arange(500)
ax[1].plot(x, pySPM.utils.Gauss(x,111,10)+pySPM.utils.Gauss(x,327,10));
pySPM.utils.Xdist(ax[1],111,327, 0.02); # plot and highlight distance

ax[2].plot(x, np.cos(x/10));
axb = pySPM.utils.DualPlot(ax[2]); # automatically create a dual plot and paint legend with correct colors
axb.plot(x, np.sin(x/10), 'C1');


Saving / Loading of Data / Variables

Python objects can be saved and retrieved with this utility. Data are also compressed with DEFLATE (zip) so that it saves space on disk. It can be usefull to save reconstruction of data (which is time consuming).


In [48]:
TOF = pySPM.ITA(get_data("BigSmiley.ita"))
Au,_ = TOF.getAddedImageByName('Au')

pySPM.utils.save('PySPM-DOC',A=12.123)
pySPM.utils.save('PySPM-DOC',B=7,Au=Au)

ax = pySPM.utils.sp(2)
Au.show(ax=ax[0], flip=True);

Au2 = pySPM.utils.load('PySPM-DOC','Au')
Au2.show(ax=ax[1], flip=True);
print(pySPM.utils.load('PySPM-DOC','B'))


7

In [49]:
import datetime
print(datetime.datetime.now())


2018-12-03 17:16:02.031914